package uploader;

import ch.ethz.ssh2.*;
import java.io.File;
import java.io.IOException;

/**
 * This is a scp client for connecting the remote server, and uploading files to
 * the remote server.
 *
 * @author zhangqunshi@126.com
 * @version java1.8, 2013-7-16
 */
public class ScpClient {

    private final String usr;
    private final String pwd;
    private final String remoteHostIp;

    /**
     * Only init this object, and not connect to server. <br>
     * TODO Should I change to connect pool?
     *
     * @param serverIp remote server IP address
     * @param username scp login username
     * @param password scp login password
     */
    public ScpClient(String serverIp, String username, String password) {
        this.usr = username;
        this.pwd = password;
        this.remoteHostIp = serverIp;
    }

    /**
     * 把给定的本地文件或者目录(包括里面所有的内容)上传到远程服务器上
     *
     * @param localFilePath
     * @param remotePath
     * @throws IOException
     */
    public void doUpload(String localFilePath, String remotePath)
            throws IOException {

        // 为了防止上传空字符串文件名,出现过类似问题
        localFilePath = localFilePath.trim();
        remotePath = remotePath.trim();

        if ("".equals(localFilePath)) {
            throw new IOException("Local file path is required");
        }
        if ("".equals(remotePath)) {
            throw new IOException("Local file path is required");
        }

        Connection conn = null;
        try {
            conn = new Connection(remoteHostIp);
            // TODO: 如果网络有问题, 连接超时有点长
            // 是不是让用户设定连接超时时间比较好些?
            conn.connect();
            boolean isAuthed = conn.authenticateWithPassword(usr, pwd);
            System.out.println("isAuthed: " + isAuthed);

            File localPath = new File(localFilePath);

            if (localPath.isDirectory()) {
                uploadDir(conn, localFilePath, remotePath, "0644");
            } else {
                System.out.println("uploading: " + localFilePath
                        + " to " + remotePath);
                SCPClient client = conn.createSCPClient();
                client.put(localFilePath, remotePath);
                System.out.println(" -> OK");
            }
        } catch (IOException e) {
            System.out.println("Fail to upload: " + e.getMessage());
            throw e;
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
    }

    /**
     * 上传一个目录到远处服务器上 (包括目录中的所有文件和子目录)
     *
     * @param conn
     * @param localDirName 本地目录名称
     * @param remotePath 要上传的远程路径
     * @param mode 文件权限
     * @throws IOException
     */
    private void uploadDir(Connection conn,
            String localDirName,
            String remotePath,
            String mode) throws IOException {

        File localDir = new File(localDirName);

        String remoteDir = remotePath + "/" + localDir.getName();
        createDir(conn, remoteDir);

        File[] files = localDir.listFiles();
        for (File localFile : files) {
            if (localFile.isDirectory()) {
                // 如果是目录,则递归进入此目录进行操作
                uploadDir(conn, localFile.getAbsolutePath(), remoteDir, mode);
            } else {
                // 如果是文件,则直接上传
                SCPClient cli = conn.createSCPClient();
                System.out.print("uploading: " + localFile.getAbsolutePath()
                        + " to " + remoteDir);
                cli.put(localFile.getAbsolutePath(), remoteDir, mode);
                System.out.println(" -> OK");
            }

        }

    }

    /**
     * Create one directory in the remote server.
     *
     * @param conn
     * @param remoteDir
     * @throws IOException
     */
    private void createDir(Connection conn, String remoteDir)
            throws IOException {
        Session s = conn.openSession();
        try {
            System.out.println("create dir: " + remoteDir);
            s.execCommand("mkdir -p -m 755 " + remoteDir);
            s.waitForCondition(ChannelCondition.EOF, 0);
        } finally {
            s.close();
        }

    }

}
